﻿using repliback4.Xml;
using System.Xml;
using System;
using repliback4.Net.Protocol.SASLMechanisms;
using System.Collections.Generic;
using Tools.Debugging;
//using System.Linq;
using repliback4.Xml.Extensions;

namespace repliback4.Net.Protocol.Messages {
    [Attributes.RequestStanza( "mechanisms", JabberNamespaces.SASL, typeof( SASLMechanisms ) )]
    public class SASLMechanisms : Stanza {
        public SASLMechanisms ( XmlDocument xDoc ) : base( xDoc ) { }
        public SASLMechanisms ( XmlElement element ) : base( element ) { }

        public void AddMechanism ( String name ) {
            this.Xml.AppendChild( this.xdoc.CreateElement( "mechanism", JabberNamespaces.SASL ) ).AppendChild( this.xdoc.CreateTextNode( name ) );
        }
    }

    [Attributes.RequestStanza( "auth", JabberNamespaces.SASL, typeof( SASLAuth ) )]
    public class SASLAuth : Stanza {

        public SASLAuth ( XmlDocument xDoc ) : base( xDoc ) { }
        public SASLAuth ( XmlElement element ) : base( element ) { }


        public String Type {
            get { return this.GetMechanismType( ); }
            set { this.SetMechanismType( value ); }
        }

        public String Message {
            get { return this.Xml.InnerText; }
            set { this.Xml.InnerText = value; }
        }

        private void SetMechanismType ( String value ) {
            this.Xml.SetAttribute( "mechanism", value );
        }

        private String GetMechanismType ( ) {
            String mechanism = this.Xml.GetAttribute( "mechanism" ).ToUpper( );
            if ( mechanism.Length != 0 ) {
                return mechanism;
            } else {
                throw new Exception( "no mechanism defined" );
            }
        }


    }

    //<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
    [Attributes.RequestStanza( "success", JabberNamespaces.SASL, typeof( SASLAuthSuccess ) )]
    public class SASLAuthSuccess : Stanza {
        public SASLAuthSuccess ( XmlDocument xDoc ) : base( xDoc ) { }
        public SASLAuthSuccess ( XmlElement element ) : base( element ) { }

    }


    //not-authorized
    [Attributes.RequestStanza( "failure", JabberNamespaces.SASL, typeof( SASLAuthFailure ) )]
    public class SASLAuthFailure : Stanza {


        static FailureConditionRegistry conditions = new FailureConditionRegistry( );
        static SASLAuthFailure ( ) {

            //Console.WriteLine( "{0} ::registering failure conditions", typeof(SASLAuthFailure).Name);
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.Aborted,
                XQName = new XmlQualifiedName( "aborted", JabberNamespaces.SASL )
            } );
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.IncorrectEncoding,
                XQName = new XmlQualifiedName( "incorrect-encoding", JabberNamespaces.SASL )
            } );
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.InvalidAuthzid,
                XQName = new XmlQualifiedName( "invalid-authzid", JabberNamespaces.SASL )
            } );
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.InvalidMechanism,
                XQName = new XmlQualifiedName( "invalid-mechanism", JabberNamespaces.SASL )
            } );
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.MechanismTooWeak,
                XQName = new XmlQualifiedName( "mechanism-too-weak", JabberNamespaces.SASL )
            } );
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.NotAuthorized,
                XQName = new XmlQualifiedName( "not-authorized", JabberNamespaces.SASL )
            } );
            conditions.Register( new FailureConditionEntry( ) {
                FailureCondition = FailureConditions.TemporaryAuthFailure,
                XQName = new XmlQualifiedName( "temporary-auth-failure", JabberNamespaces.SASL )
            } );

            //aborted
            //incorrect-encoding
            //invalid-authzid
            //invalid-mechanism
            //mechanism-too-weak
            //not-authorized
            //temporary-auth-failure
        }


        public SASLAuthFailure ( XmlDocument xDoc ) : base( xDoc ) { }
        public SASLAuthFailure ( XmlElement element ) : base( element ) { }

        public FailureConditions Condition {
            get { return this.GetCondition( ); }
            set { this.SetCondition( value ); }
        }

        private void SetCondition ( FailureConditions value ) {
            this.Xml.RemoveAll( );

            this.Xml.AppendChild( this.xdoc.CreateElement( conditions.LookupByCondition( value ).Name, Namespaces.SASL ) );
        }

        private FailureConditions GetCondition ( ) {
            return conditions.LookupByName( this.Xml.FirstTypedChild( XmlNodeType.Element ).Name );
        }

    }

    //urn:ietf:params:xml:ns:xmpp-tls : starttls
    //urn:ietf:params:xml:ns:xmpp-tls : proceed

    [Attributes.RequestStanza( "starttls", Namespaces.TLS, typeof( StartTLS ) )]
    public class StartTLS : Stanza {
        public StartTLS ( XmlDocument xDoc ) : base( xDoc ) { }
        public StartTLS ( XmlElement element ) : base( element ) { }

        public Boolean Required {
            get { return this.GetRequired( ); }
            set { this.SetRequired( value ); }
        }

        private void SetRequired ( bool value ) {
            this.Xml.RemoveAll( );
            if ( value ) {
                this.Xml.AppendChild( this.xdoc.CreateElement( "required", Namespaces.TLS ) );
            }
        }

        private bool GetRequired ( ) {
            return this.Xml.GetElementsByTagName( "required", Namespaces.TLS ).Count != 0;
        }

    }
    [Attributes.RequestStanza( "proceed", Namespaces.TLS, typeof( ProceedTLS ) )]
    public class ProceedTLS : Stanza {
        public ProceedTLS ( XmlDocument xDoc ) : base( xDoc ) { }
        public ProceedTLS ( XmlElement element ) : base( element ) { }
    }


    //    <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
    //<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
    [Attributes.RequestStanza( "bind", JabberNamespaces.JABBER_BIND, typeof( Bind ) )]
    public class Bind : Stanza {
        public Bind ( XmlDocument xDoc ) : base( xDoc ) { }
        public Bind ( XmlElement element ) : base( element ) { }

        public String Resource {
            set { this.SetResource( value ); }
            get { return this.GetResource( ); }
        }



        public String Identification {
            set { this.SetIdentification( value ); }
            get { return this.GetIdentification( ); }
        }

        private string GetIdentification ( ) {
            return this.GetField( "jid" );
        }

        private void SetIdentification ( string value ) {
            this.SetField( "jid", value );
        }


        private void SetResource ( string value ) {
            this.SetField( "resource", value );
        }

        private string GetResource ( ) {
            return this.GetField( "resource" );
        }


        private string GetField ( String xqname ) {
            XmlQualifiedName x = new XmlQualifiedName( xqname );
            String xNamespace = x.Namespace != "" ? x.Namespace : this.Xml.NamespaceURI;

            XmlNodeList list = this.Xml.GetElementsByTagName( x.Name, xNamespace );
            if ( list.Count > 0 ) {
                return list[0].InnerText;
            } else {
                return "";
            }
        }

        private void ClearField ( String xqname ) {
            XmlQualifiedName x = new XmlQualifiedName( xqname );
            String xNamespace = x.Namespace != "" ? x.Namespace : this.Xml.NamespaceURI;

            XmlNodeList list = this.Xml.GetElementsByTagName( x.Name, xNamespace );
            foreach ( XmlNode n in list ) {
                this.Xml.RemoveChild( n );
            }
        }
        private void AddField ( String xqname, String value ) {
            XmlQualifiedName x = new XmlQualifiedName( xqname );
            String xNamespace = x.Namespace != "" ? x.Namespace : this.Xml.NamespaceURI;

            this.Xml.AppendChild( this.xdoc.CreateElement( x.Name, xNamespace ) ).InnerText = value;
        }

        private void SetField ( String xqname, String value ) {

            this.ClearField( xqname );
            this.AddField( xqname, value );
        }

    }

    [Attributes.RequestStanza( "session", JabberNamespaces.JABBER_SESSION, typeof( Session ) )]
    public class Session : Stanza {
        public Session ( XmlDocument xDoc ) : base( xDoc ) { }
        public Session ( XmlElement element ) : base( element ) { }
    }



    [Attributes.RequestStanza( "iq", JabberNamespaces.JABBER_CLIENT, typeof( InfoQuery ) )]
    public class InfoQuery : Stanza {

        static InfoQuery ( ) {
            types = new System.Collections.Generic.Dictionary<IqTypes, string>( );

            types.Add( IqTypes.Error, "error" );
            types.Add( IqTypes.Get, "get" );
            types.Add( IqTypes.Set, "set" );
            types.Add( IqTypes.Result, "result" );

        }
        protected Stanza query;
        public enum IqTypes { Get, Set, Result, Error }
        protected static System.Collections.Generic.Dictionary<IqTypes, string> types;
        public InfoQuery ( XmlDocument xDoc )
            : base( xDoc ) {
            this.Id = Guid.NewGuid( ).ToString( );
        }
        public InfoQuery ( XmlElement element ) : base( element ) { }

        public IqTypes Type {
            set {
                this.Xml.SetAttribute( "type", types[value] );
            }
            get {
                switch ( this.Xml.GetAttribute( "type" ) ) {
                    case "error": return IqTypes.Error;
                    case "get": return IqTypes.Get;
                    case "set": return IqTypes.Set;
                    case "result": return IqTypes.Result;
                    default: throw new Exception( "undefined type" );
                }
            }
        }

        public String Id {
            set {
                this.Xml.SetAttribute( "id", value );
            }
            get {
                return this.Xml.GetAttribute( "id" );
            }
        }
        public Stanza Query {
            get { return this.GetQuery( ); }
            set { this.SetQuery( value ); }
        }

        public Stanza GetQuery ( ) {
            if ( this.query != null ) {
                return this.query;
            } else {
                if ( this.Xml.FirstTypedChild( XmlNodeType.Element ) != null ) {
                    try {
                        return this.Registry.GetTag( this.Xml.FirstTypedChild(XmlNodeType.Element)) as Stanza;

                    } catch ( Exception  ) {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            //throw new NotImplementedException( );
        }

        private void SetQuery ( Stanza value ) {
            this.query = value;
            this.Xml.RemoveAll( );
            this.Xml.AppendChild( value.Xml );
            //throw new NotImplementedException( );
        }
        public Boolean HasQuery {
            get { return this.Xml.FirstTypedChild( XmlNodeType.Element ) != null; }
        }
    }


    // <presence from="thesebas@jabber.aster.pl/theamilo" type="unavailable" to="sszymbor@gmail.com" >
    //<status>Disconnected</status>
    //</presence>
    [Attributes.RequestStanza( "presence", JabberNamespaces.JABBER_CLIENT, typeof( Presence ) )]
    public class Presence : Stanza {
        public Presence ( XmlDocument xDoc ) : base( xDoc ) { }
        public Presence ( XmlElement element ) : base( element ) { }
    }


    //<message type="chat" to="thesebas@jabber.wp.pl/Psi" id="aabba" >
    //<body>hi</body>
    //<active xmlns="http://jabber.org/protocol/chatstates"/>
    //</message>
    [Attributes.RequestStanza( "message", JabberNamespaces.JABBER_CLIENT, typeof( Message ) )]
    public class Message : Stanza {
        public Message ( XmlDocument xDoc ) : base( xDoc ) { }
        public Message ( XmlElement element ) : base( element ) { }
    }





    //[Attributes.RequestStanza( "login", "", typeof( Login ) )]
    //public class Login : Stanza {
    //    public Login ( XmlDocument xDoc ):base(xDoc) {
    //        //this.userName = userName;
    //        //this.password = password;
    //    }
    //    public Login
    //}
}
